﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Collections;
using System.Data.SqlClient;
using KegCommunication;
using KegData;

namespace KegMonitor
{
    public partial class Form1 : Form
    {
        Dictionary<string, int> _meters = new Dictionary<string, int>() { 
            { "SwisFlow", 6000},
            { "Custom", 0}};

        FormWindowState savedState = FormWindowState.Normal;
        bool IsTray = false;
        bool waitingOnPing = false;

        public Form1()
        {
            InitializeComponent();

            Program.KegBot.StatusChange += new StatusEventHandler(KegBotStatusChanged);
            Program.KegBot.Hello += new HelloEventHandler(PingReceived);
        }

        private delegate void UpdateStatusDelegate(object sender, StatusEventArgs e);
        private void KegBotStatusChanged(object sender, StatusEventArgs e)
        {
            if (this.lblControlStatus.InvokeRequired) 
            {
                UpdateStatusDelegate updateCallBack = new UpdateStatusDelegate(KegBotStatusChanged); 
                this.Invoke(updateCallBack, new object[] { sender, e }); 
            } 
            else 
            {
                lblControlStatus.Text = e.NewStatus.ToString() + "  (" + Properties.Settings.Default.Port + ")";
                lblControlStatus2.Text = KegReader.GetStatusText(e.NewStatus);
                
                // 
                if (e.NewStatus == KegBotStatus.Running || e.NewStatus == KegBotStatus.ErrorTimeOut)
                {
                    this.TrayIcon.Icon = Properties.Resources.running;
                    btnControlStart.Enabled = false;
                    btnControlStop.Enabled = true;
                }
                else
                {
                    this.TrayIcon.Icon = Properties.Resources.stopped;
                    btnControlStart.Enabled = true;
                    btnControlStop.Enabled = false;
                }
            } 
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // Bind the log to the log list
            listLog.DataSource = KegLog.Entries;

            LoadServerSettings();
            LoadKegBotSettings();

            LoadPlugins();
        }

        private void buttonTemp_Click(object sender, EventArgs e)
        {
            string OneWireId = comboBoxOneWire.Text;
            double Reading = Convert.ToDouble(textBoxTempValue.Text);

            Program.KegBot.SendTestTemperature(OneWireId, Reading);
        }

        private void buttonPour_Click(object sender, EventArgs e)
        {
            int UserId = 1;
            byte Tap = Convert.ToByte(comboBoxTaps.SelectedItem);
            double Volume = Convert.ToDouble(textBoxPourVolume.Text);

            Program.KegBot.SendTestPour(Tap, Volume, UserId, 50, 100);
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                e.Cancel = true;
                ToggleTrayMode();
            }
        }

        private void ToggleTrayMode()
        {
            if (IsTray)
            {
                this.WindowState = savedState;
                this.Show();
                IsTray = false;
            }
            else
            {
                savedState = this.WindowState;
                this.Hide();
                IsTray = true;
                TrayIcon.ShowBalloonTip(5000, "KegMonitor", "KegMonitor is still running.", ToolTipIcon.Info);
            }
        }

        private void LoadServerSettings()
        {
            // Database server settings
            tbServerDatabase.Text = Properties.Settings.Default.dbDatabase;
            tbServerName.Text = Properties.Settings.Default.dbServer;
            tbServerPass.Text = Properties.Settings.Default.dbPassword;
            tbServerUser.Text = Properties.Settings.Default.dbUser;
        }

        private void SaveServerSettings()
        {
            // Database server
            Properties.Settings.Default.dbDatabase = tbServerDatabase.Text;
            Properties.Settings.Default.dbServer = tbServerName.Text;
            Properties.Settings.Default.dbPassword = tbServerPass.Text;
            Properties.Settings.Default.dbUser = tbServerUser.Text;
        }

        private void LoadPlugins()
        {
            foreach (PluginStatus<IListenerPlugin> iplugin in Program.KegBot.ListenerPlugins)
            {
                listIPlugins.Items.Add(iplugin);
            }

            foreach (PluginStatus<IAuthenticatorPlugin> aplugin in Program.KegBot.AuthenticatePlugins)
            {
                listAPlugins.Items.Add(aplugin);
            }

            // TODO: Check (enable) any plugins accoridng to the settings
        }

        private void LoadTestSettings()
        {
        }

        private void LoadKegBotSettings()
        {
            // Get the ports available on the system
            string[] ports = System.IO.Ports.SerialPort.GetPortNames();
            cmbKegBotPort.DataSource = ports;
            cmbKegBotPort.SelectedItem = Properties.Settings.Default.Port; // TODO: Not sure if this works, only one com port right now

            // List the known flowmeters
            cmbFlowMeters.DataSource = new BindingSource(_meters, null);
            cmbFlowMeters.DisplayMember = "Key";
            cmbFlowMeters.ValueMember = "Value";

            // Select the item that corresponds to the name
            // TODO: do this

            // Load the settings into the boxes for editing.
            tbPourDelay.Text = Properties.Settings.Default.PourDelay.ToString();
            tbMinimumPourVolume.Text = Properties.Settings.Default.PourMinimum.ToString();
        }

        private void SaveKegBotSettings()
        {
            // Store the value to their respective properties
            Properties.Settings.Default.PourDelay = Convert.ToInt32(tbPourDelay.Text);
            Properties.Settings.Default.PourMinimum = Convert.ToDouble(tbMinimumPourVolume.Text);
            Properties.Settings.Default.FlowTicksPerLiter = Convert.ToInt32(tbTicksPerLiter.Text);

            // Save the name of the flowmeter for lookup later
            KeyValuePair<string, int> de = (KeyValuePair<string, int>)cmbFlowMeters.SelectedItem;
            Properties.Settings.Default.FlowSensorType = de.Key.ToString();

            // Save those value permanently
            Properties.Settings.Default.Save();
        }

        private void Form1_SizeChanged(object sender, EventArgs e)
        {
            // If the window is becoming minimized
            if (WindowState == FormWindowState.Minimized)
            {
                ToggleTrayMode();
            }
        }

        private void TrayIcon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            ToggleTrayMode();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void configureToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ToggleTrayMode();
        }

        private void startKegManagerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Start the KegManager application
            Process kegmanager = new Process();
            kegmanager.StartInfo.FileName = "KegManager.exe";

            try
            {
                kegmanager.Start();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Unable to find KegManager.exe.", "Error", MessageBoxButtons.OK);
                // TODO: Open the options dialog to set this path.
            }
        }

        private void btnServerClear_Click(object sender, EventArgs e)
        {
            Confirmation confirm = new Confirmation("Erase All Data?", "Are you sure you'd like to erase all of the data in the database (beers, kegs, pour, and temperature data)?", "INDUBITABLY");
            DialogResult result = confirm.ShowDialog(this);
            if (result == DialogResult.OK)
            {
                // Lock on the data context, to avoid other threads from trying to insert/update items while we drop
                lock (KegWriter.DataSource)
                {
                    if (KegWriter.DataSource.DatabaseExists())
                    {
                        KegWriter.DataSource.DeleteDatabase();
                    }
                    KegWriter.DataSource.CreateDatabase();
                }
            }
        }

        private void cmbFlowMeters_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Load the correct ticks value into the text box
            KeyValuePair<string, int> de = (KeyValuePair<string, int>)cmbFlowMeters.SelectedItem;

            if (de.Key == "Custom")
            {
                tbTicksPerLiter.Enabled = true;
            }
            else
            {
                tbTicksPerLiter.Enabled = false;
                tbTicksPerLiter.Text = de.Value.ToString();
            }
        }

        private void btnKegBotRevert_Click(object sender, EventArgs e)
        {
            LoadKegBotSettings();
        }

        private void btnKegBotApply_Click(object sender, EventArgs e)
        {
            SaveKegBotSettings();
        }

        private void btnTestConnection_Click(object sender, EventArgs e)
        {
            SqlConnection myConnection = new SqlConnection(String.Format(
                                       "user id={0};" +
                                       "password={1};" + 
                                       "server={2};" +
                                       //"Trusted_Connection=yes;" +
                                       "database={3}; " +
                                       "connection timeout=30",
                                       tbServerUser.Text,
                                       tbServerPass.Text,
                                       tbServerName.Text,
                                       tbServerDatabase.Text));
            try
            {
                myConnection.Open();
                MessageBox.Show("Successfully connected to database", "Success!");
                myConnection.Close();
                btnServerApply.Enabled = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error connecting to database");
                btnServerApply.Enabled = false;
            }
        }

        private void chkIncomingPackets_CheckedChanged(object sender, EventArgs e)
        {
            // Add a new handler to OnPacket on the kegbot that will write to the packet list box
            if (chkIncomingPackets.Checked == true)
            {
                Program.KegBot.Packet += new PacketEventHandler(RecordPacket);
            }
            else
            {
                Program.KegBot.Packet -= new PacketEventHandler(RecordPacket);
            }
        }

        private delegate void PacketDelegate(object sender, PacketEventArgs e);
        private void RecordPacket(object sender, PacketEventArgs e)
        {
            // Recall tis function using an Invoke if it is called by the non-UI thread.
            if (this.lbIncomingPackets.InvokeRequired)
            {
                PacketDelegate packetCallBack = new PacketDelegate(RecordPacket);
                this.Invoke(packetCallBack, new object[] { sender, e });
            }
            else
            {
                // Add the packet to the list box
                string item = String.Format("[{0:hh:mm:ss}]", DateTime.Now);
                switch (e.Type)
                {
                    case PacketType.Hello:
                        HelloPacket hp = new HelloPacket();
                        hp.ParsePacket(e.Packet);
                        item += hp.ToString();
                        break;
                    case PacketType.FlowMeter:
                        FlowMeterPacket fmp = new FlowMeterPacket();
                        fmp.ParsePacket(e.Packet);
                        item += fmp.ToString();
                        break;
                    case PacketType.Thermometer:
                        TemperaturePacket tp = new TemperaturePacket();
                        tp.ParsePacket(e.Packet);
                        item += tp.ToString();
                        break;
                    case PacketType.Presence:
                    case PacketType.OutputStatus:
                        // TODO: Implement
                        throw new NotImplementedException();
                        break;
                    default:
                        item += String.Format("Unknown Packet: {0}", Helper.ByteArrayToStr(e.Packet));
                        break;
                }

                // Remove an item if the list exceeds 250 items
                if (lbIncomingPackets.Items.Count >= 250)
                {
                    lbIncomingPackets.Items.RemoveAt(lbIncomingPackets.Items.Count - 1);
                }
                lbIncomingPackets.Items.Insert(0, item);    
            } 

        }

        private void btnControlClear_Click(object sender, EventArgs e)
        {
            lbIncomingPackets.Items.Clear();
        }

        private void btnControlStop_Click(object sender, EventArgs e)
        {
            Program.StopKegMonitor();
        }

        private void btnControlStart_Click(object sender, EventArgs e)
        {
            // Save the COM port from the list
            Properties.Settings.Default.Port = cmbKegBotPort.SelectedValue.ToString();
            Properties.Settings.Default.Save();
            Program.KegBot.Port = cmbKegBotPort.SelectedValue.ToString();

            Program.StartKegMonitor();
        }

        private void cmbKegBotPort_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Stop the kegmonitor
            //Program.StopKegMonitor();

            // Change the port
           // Properties.Settings.Default.Port = cmbKegBotPort.SelectedValue.ToString();
            //Properties.Settings.Default.Save();
        }

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Reload the log datasource since it doesn't seem to atomatically do this
            // even with IBindingList
            listLog.DataSource = null;
            listLog.DataSource = KegLog.Entries;
        }

        private void btnLogClear_Click(object sender, EventArgs e)
        {
            KegLog.Entries.Clear();
            listLog.DataSource = null;
            listLog.DataSource = KegLog.Entries;

            //listLog.Items.Clear();
        }

        private void btnControlPing_Click(object sender, EventArgs e)
        {
            waitingOnPing = true;
            Program.KegBot.Ping();
        }

        private void PingReceived(object sender, HelloEventArgs e)
        {
            // Only respond to the ping when we're waiting for one
            if (waitingOnPing)
            {
                waitingOnPing = false;

                MessageBox.Show("Received a response from the KegBot (Firmware Version " + e.Version + ").",
                    "KegBot Response",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information);
            }
        }

        private void btnServerApply_Click(object sender, EventArgs e)
        {
            SaveServerSettings();

            // Re-initialize the kegwriter with the new connection data.
            KegWriter.Initialize(Helper.ConnectionString);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Populate taps
            KegeratorDataContext dataContext = KegWriter.DataSource;
            comboBoxTaps.Items.Clear();
            var Taps = (from t in dataContext.Taps
                        select t);
            foreach (Tap t in Taps)
            {
                comboBoxTaps.Items.Add(t.Position);
            }

            // Populate OneWires
            comboBoxOneWire.Items.Clear();
            var TemperatureLocations = (from l in dataContext.TemperatureLocations
                                        select l);
            foreach (TemperatureLocation t in TemperatureLocations)
            {
                comboBoxOneWire.Items.Add(t.OneWireId);
            }

           gbPour.Controls.Cast<Control>().ToList<Control>().ForEach(s => s.Enabled = true);
           gbTemp.Controls.Cast<Control>().ToList<Control>().ForEach(s => s.Enabled = true);

           dataContext.Connection.Close();
        }

        private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PluginStatus<IListenerPlugin> iplugin = listIPlugins.SelectedItem as PluginStatus<IListenerPlugin>;
            iplugin.Plugin.Settings();
        }

        private void listIPlugins_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            // Enable this item
            PluginStatus<IListenerPlugin> iplugin = listIPlugins.Items[e.Index] as PluginStatus<IListenerPlugin>;
            if (e.NewValue == CheckState.Checked)
            {
                // Enable the plugin
                iplugin.IsEnabled = true;
            }
            else
            {
                // Disable the plugin
                iplugin.IsEnabled = false;
            }

            Program.KegBot.ConnectPlugin(iplugin);
        }
    }
}
